import json
import logging
import re

from django.conf import settings
from django.contrib.auth import login, logout
from django.http import JsonResponse
from django.views import View
from django_redis import get_redis_connection

from carts.utils import merge_cart_cookie_to_redis
from celery_task.tasks.email import send_verify_email
from users.models import User, Address
from users.utils import auth_user, generate_verify_email_token, check_verify_email_token
from meiduo.utils.views import LoginRequiredMixin
from goods.models import SKU,SKUImage

logger = logging.getLogger('django')


# Create your views here.

class UsernameCountView(View):

    def get(self, request, username):
        """
        查询用户名是否已存在
        :param request: 请求对象
        :param username: 用户名
        :return: JSON
        """
        count = User.objects.filter(username=username).count()  # 从数据库查询用户名是否存在
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'count': count})


class MobileCountView(View):

    def get(self, request, mobile):
        """
        查询手机号是否已存在
        :param request: 请求对象
        :param mobile: 手机号
        :return: JSON
        """
        count = User.objects.filter(mobile=mobile).count()  # 从数据库查询号码是否存在
        return JsonResponse({'code': 0, 'errmsg': 'OK', 'count': count})


class RegisterView(View):

    def post(self, request):
        """
        注册用户
        :param request: 请求对象
        :return: JSON
        """
        '''获取请求参数'''
        request_data = json.loads(request.body)  # 获取请求体数据并转换成字典类型
        username = request_data.get("username")  # 获取用户输入账号
        password = request_data.get("password")  # 获取用户输入密码
        password2 = request_data.get("password2")  # 获取用户二次确认密码
        mobile = request_data.get("mobile")  # 获取用户输入手机号
        sms_code = request_data.get("sms_code")  # 获取用户输入的短信验证码
        allow = request_data.get("allow")  # 获取allow的值

        '''检查请求参数'''
        if not all([username, password, password2, mobile, sms_code, isinstance(allow, bool), allow]):
            return JsonResponse({'code': 400, 'errmsg': '请求参数有误'})

        '''比对短信验证码'''
        redis_conn = get_redis_connection("verify_code")  # 根据django缓存配置"verify_code"获取redis的2号库对象
        sms_check_code = redis_conn.get(f"sms_code_{mobile}")  # 从数据库获取短信验证码
        if not sms_check_code:  # 验证码不存在
            return JsonResponse({'code': 400, 'errmsg': '短信验证码过期'})
        if sms_code != sms_check_code.decode():  # 用户输入的验证码与数据库存储的验证码不匹配
            return JsonResponse({'code': 400, 'errmsg': '短信验证码错误'})

        '''检查用户名格式'''
        if re.match(r"^[0-9]+$", username) or not re.match(r"^[a-zA-Z0-9_-]{5,20}$", username):  # 检查用户名格式
            return JsonResponse({'code': 400, 'errmsg': '用户名格式错误'})

        '''检查密码格式，并比对两个密码是否相同'''
        if not (re.match(r"^[a-zA-Z0-9_]{8,20}$", password) and
                re.match(r"^[a-zA-Z0-9_]{8,20}$", password2)):  # 检查两次输入的密码的格式
            return JsonResponse({'code': 400, 'errmsg': '密码格式错误'})
        if password != password2:  # 判断两次输入的密码是否一致
            return JsonResponse({'code': 400, 'errmsg': '两次输入的密码不一致'})

        '''检查手机号格式'''
        if not re.match(r"^1[3-9]\d{9}$", mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号码格式错误'})

        '''存储用户信息'''
        try:
            user = User.objects.create_user(  # 创建用户模型对象向mysql数据库存入用户信息
                username=username,
                password=password,
                mobile=mobile,
            )
        except Exception as e:
            logger.error(f"用户信息存储失败：{e}")
            return JsonResponse({'code': 400, 'errmsg': '注册失败'})

        '''设置状态保持'''
        response = JsonResponse({'code': 0, 'errmsg': '注册成功'})  # 创建响应对象
        # request.session['username'] = user.username  # 使用session记录用户登陆信息
        login(request, user)  # 通过django内置login方法记录用户登陆信息
        request.session.set_expiry(0)  # 设置用户登陆信息在浏览器关闭之后删除
        response.set_cookie("username", user.username)  # 设置cookie，浏览器关闭之前有效

        '''返回注册成功响应'''
        return response


class LoginView(View):

    def post(self, request):
        """
        用户登录
        :param request: 请求对象
        :return: JSON
        """
        '''获取请求参数'''
        request_data = json.loads(request.body)  # 获取请求体数据并转换成字典类型
        username = request_data.get("username")  # 获取用户输入账号
        password = request_data.get("password")  # 获取用户输入密码
        remembered = request_data.get("remembered")  # 获取remembered的值

        '''检查请求参数'''
        if not all([username, password]):
            return JsonResponse({'code': 400, 'errmsg': '请求参数有误'})

        '''验证用户账号及密码'''
        user = auth_user(username, password)  # 用户验证
        if user is None:
            return JsonResponse({'code': 400, 'errmsg': '账号或密码有误'})

        '''设置状态保持'''
        response = JsonResponse({'code': 0, 'errmsg': '登录成功'})  # 创建响应对象
        # request.session['username'] = user.username  # 使用session记录用户登陆信息
        login(request, user)  # 通过django内置login方法记录用户登陆信息
        if remembered:  # 判断用户是否要记住登录状态
            request.session.set_expiry(None)  # 用户登陆信息保存两周
            response.set_cookie("username", user.username, max_age=60 * 60 * 24 * 14)  # 设置cookie，有效时间为两周
        else:
            request.session.set_expiry(0)  # 用户登陆信息在浏览器关闭之后删除
            response.set_cookie("username", user.username)  # 设置cookie，浏览器关闭之前有效

        '''返回登录成功响应'''
        response = merge_cart_cookie_to_redis(request, response)  # 合并购物车数据
        return response


class LogoutView(View):

    def delete(self, request):
        """
        退出登录
        :param request: 请求对象
        :return: JSON
        """
        response = JsonResponse({'code': 0, 'errmsg': '退出成功'})  # 创建响应对象
        logout(request)  # 清除用户登录信息
        response.delete_cookie("username")  # 删除cookie
        return response


class UserInfoView(LoginRequiredMixin, View):

    def get(self, request):
        """
        获取用户中心数据
        :param request: 请求对象
        :return: JSON
        """
        return JsonResponse({
            'code': 0,
            'errmsg': 'OK',
            'info_data': {
                'username': request.user.username,
                'mobile': request.user.mobile,
                'email': request.user.email,
                'email_active': request.user.email_active
            }
        })


class EmailView(View):

    def put(self, request):
        """
        添加邮箱
        :param request: 请求对象
        :return: JSON
        """
        '''获取请求参数'''
        request_data = json.loads(request.body)  # 获取请求提参数
        email = request_data.get("email")

        '''检查请求参数'''
        if not email:
            return JsonResponse({'code': 400, 'errmsg': '请求参数有误'})

        '''检查邮箱格式'''
        if not re.match(r'^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', email):
            return JsonResponse({'code': 400, 'errmsg': '邮箱格式有误'})

        '''保存邮箱'''
        user = request.user
        if User.objects.filter(email=email).count() == 0:  # 判断邮箱是否已存在
            try:
                user.email = email  # 设置邮箱
                user.save()  # 保存邮箱
            except Exception as e:
                logger.error(e)
                return JsonResponse({'code': 400, 'errmsg': '邮箱保存失败'})

        '''发送邮箱验证邮件'''
        token = generate_verify_email_token(user.username, user.email)  # 生成邮箱验证token
        verify_url = settings.EMAIL_VERIFY_URL + token  # 拼接邮箱验证url
        send_verify_email.delay(email, verify_url)  # 发布异步任务（需先启动异步任务捕获），发送验证邮件

        '''返回保存成功响应'''
        return JsonResponse({'code': 0, 'errmsg': '邮箱保存成功'})


class VerifyEmailView(View):

    def put(self, request):
        """
        验证邮箱
        :param request: 请求对象
        :return: JSON
        """
        '''获取请求参数'''
        token = request.GET.get("token")

        '''检查请求参数'''
        if not token:
            return JsonResponse({'code': 400, 'errmsg': '请求参数有误'})

        '''解密token值'''
        name_email = check_verify_email_token(token)
        if not name_email:
            return JsonResponse({'code': 400, 'errmsg': '解密Token失败'})

        '''更改默认邮箱验证状态'''
        try:
            user = User.objects.get(username=name_email[0], email=name_email[1])  # 获取用户对象
        except Exception as e:  # 获取用户对象失败
            logger.error(e)
            return JsonResponse({'code': 400, 'errmsg': '获取用户对象失败'})
        user.email_active = True  # 邮箱验证状态更改为True
        try:
            user.save()  # 保存验证状态
        except Exception as e:  # 保存失败
            logger.error(e)
            return JsonResponse({'code': 400, 'errmsg': '邮箱验证状态更改失败'})

        '''返回验证成功响应'''
        return JsonResponse({'code': 0, 'errmsg': '邮箱验证成功'})


class CreateAddressView(LoginRequiredMixin, View):

    def post(self, request):
        """
        添加收货地址
        :param request: 请求对象
        :return: JSON
        """
        '''判断用户地址数量是否超限'''
        if Address.objects.filter(user=request.user).count() > settings.ADDRESSES_LIMIT:
            return JsonResponse({'code': 400, 'errmsg': '收货地址数量达到上限'})

        '''获取请求参数'''
        request_data = json.loads(request.body)  # byte转dict
        receiver = request_data.get("receiver")  # 收件人
        province_id = request_data.get("province_id")  # 省份id
        city_id = request_data.get("city_id")  # 城市id
        district_id = request_data.get("district_id")  # 区县id
        place = request_data.get("place")  # 详细地址
        mobile = request_data.get("mobile")  # 收件人手机号
        tel = request_data.get("tel")  # 收件人座机号
        email = request_data.get("email")  # 收件人邮箱

        '''检查请求参数'''
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400, 'errmsg': '请求参数有误'})

        '''检查参数格式'''
        if not re.match(r"^1[3-9]\d{9}$", mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号码格式有误'})
        if tel and not re.match(r"^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$", tel):
            return JsonResponse({'code': 400, 'errmsg': '固定电话格式有误'})
        if email and not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            return JsonResponse({'code': 400, 'errmsg': '邮箱格式有误'})

        '''添加地址'''
        try:
            # address = Address.objects.create(
            #     user=request.user,
            #     receiver=receiver,
            #     province=Area.objects.get(id=province_id),
            #     city=Area.objects.get(id=city_id),
            #     district=Area.objects.get(id=district_id),
            #     place=place,
            #     mobile=mobile,
            #     tel=tel,
            #     email=email
            # )

            request_data["user"] = request.user  # 添加"user"对象
            address = Address.objects.create(**request_data)  # 创建地址
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400, 'errmsg': '添加收货地址失败'})

        '''判断用户是否有默认地址'''
        if request.user.default_address is None:
            try:
                User.objects.filter(id=request.user.id).update(default_address=address)  # 设置当前地址为默认地址
            except Exception as e:
                logger.error(e)
                return JsonResponse({'code': 400, 'errmsg': '设置默认地址失败'})

        '''返回响应'''
        return JsonResponse({
            'code': 0,
            'errmsg': '添加地址成功',
            'address': {
                'id': address.id,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }
        })


class AddressView(LoginRequiredMixin, View):

    def get(self, request):
        """
        获取用户收货地址
        :param request: 请求对象
        :return: JSON
        """
        '''获取用户所有收货地址'''
        default_address = request.user.default_address  # 获取用户默认收货地址
        addresses = Address.objects.filter(user=request.user, is_deleted=False)  # 获取用户所有收货地址
        address_list = []  # 定义收货地址列表
        for address in addresses:  # 数据整理
            address_dict = {
                "id": address.id,  # 地址id
                "title": address.title,  # 标题
                "receiver": address.receiver,  # 收件人
                "province": address.province.name,  # 省
                "city": address.city.name,  # 市
                "district": address.district.name,  # 区
                "place": address.place,  # 详细地址
                "mobile": address.mobile,  # 收件人手机号
                "tel": address.tel,  # 收件人固定电话
                "email": address.email  # 收件人邮箱
            }
            try:
                if address_dict["id"] == default_address.id:  # 判断是否是默认收货地址
                    address_list.insert(0, address_dict)  # 将默认收货地址插入到列表0号位，用于置顶展示
                else:
                    address_list.append(address_dict)  # 添加地址信息到列表
            except AttributeError:  # 没有设置默认收货地址时"default_address.id"会报错
                User.objects.filter(id=request.user.id).update(default_address=address)  # 设置当前地址为默认地址

        return JsonResponse({
            'code': 0,
            'errmsg': 'OK',
            'default_address_id': request.user.default_address_id,
            'addresses': address_list,
            'limit': settings.ADDRESSES_LIMIT
        })


class UpdateDestroyAddressView(LoginRequiredMixin, View):

    def put(self, request, address_id):
        """
        修改收货地址
        :param request: 请求对象
        :param address_id: 目标地址的id
        :return: JSON
        """
        '''获取目标收货地址'''
        # try:
        #     address = Address.objects.get(id=address_id)
        # except Exception as e:
        #     logger.error(e)
        #     return JsonResponse({'code': 400, 'errmsg': '获取收货地址失败'})

        '''获取请求参数'''
        request_data = json.loads(request.body.decode())  # byte转dict
        receiver = request_data.get("receiver")
        province_id = request_data.get("province_id")
        city_id = request_data.get("city_id")
        district_id = request_data.get("district_id")
        place = request_data.get("place")
        mobile = request_data.get("mobile")
        tel = request_data.get("tel")
        email = request_data.get("email")

        '''检查请求参数'''
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400, 'errmsg': '请求参数有误'})

        '''检查参数格式'''
        if not re.match(r"^1[3-9]\d{9}$", mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号码格式有误'})
        if tel and not re.match(r"^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$", tel):
            return JsonResponse({'code': 400, 'errmsg': '固定电话格式有误'})
        if email and not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            return JsonResponse({'code': 400, 'errmsg': '邮箱格式有误'})

        '''更新收货地址'''
        try:
            # address.receiver = receiver
            # address.province = Area.objects.get(id=province_id)
            # address.city = Area.objects.get(id=city_id)
            # address.district = Area.objects.get(id=district_id)
            # address.place = place
            # address.mobile = mobile
            # address.tel = tel
            # address.email = email
            # address.save()  # 执行保存

            # 删除多余数据
            request_data.pop('province')
            request_data.pop('city')
            request_data.pop('district')
            Address.objects.filter(id=address_id).update(**request_data)  # 传递request_data更新地址
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400, 'errmsg': '更新收货地址失败'})

        '''获取更新后收货地址'''
        try:
            address = Address.objects.get(id=address_id)
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400, 'errmsg': '获取收货地址失败'})

        '''返回修改成功响应'''
        return JsonResponse({
            'code': 0,
            'errmsg': 'OK',
            'address': {
                "id": address.id,  # 地址id
                "title": address.title,  # 标题
                "receiver": address.receiver,  # 收件人
                "province": address.province.name,  # 省
                "city": address.city.name,  # 市
                "district": address.district.name,  # 区
                "place": address.place,  # 详细地址
                "mobile": address.mobile,  # 收件人手机号
                "tel": address.tel,  # 收件人固定电话
                "email": address.email  # 收件人邮箱
            }
        })

    def delete(self, request, address_id):
        """
        逻辑删除收货地址
        :param request: 请求对象
        :param address_id: 目标地址的id
        :return: JSON
        """
        result = Address.objects.filter(id=address_id).update(is_deleted=True)
        if not result:
            return JsonResponse({'code': 400, 'errmsg': '删除收货地址失败'})
        return JsonResponse({
            'code': 0,
            'errmsg': 'OK',
        })


class DefaultAddressView(LoginRequiredMixin, View):

    def put(self, request, address_id):
        """
        替换默认收货地址
        :param request: 请求对象
        :param address_id: 新收货地址的id
        :return: JSON
        """
        '''获取新收货地址'''
        try:
            address = Address.objects.get(id=address_id)
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 400, 'errmsg': '获取新收货地址失败'})

        '''更新用户的默认收货地址'''
        result = User.objects.filter(id=request.user.id).update(default_address=address)
        if not result:
            return JsonResponse({'code': 400, 'errmsg': '更新默认收货地址失败'})
        return JsonResponse({'code': 0, 'errmsg': 'OK'})


class UpdateTitleAddressView(LoginRequiredMixin, View):

    def put(self, request, address_id):
        """
        修改收货地址标题
        :param request: 请求对象
        :param address_id: 目标地址的id
        :return: JSON
        """
        request_data = json.loads(request.body)  # 获取请求参数
        title = request_data.get("title")  # 获取新标题
        result = Address.objects.filter(id=address_id).update(title=title)  # 更新收货地址的标题
        if not result:
            return JsonResponse({'code': 400, 'errmsg': '更新收货地址标题失败'})
        return JsonResponse({'code': 0, 'errmsg': 'OK'})


class ChangePasswordView(LoginRequiredMixin, View):

    def put(self, request):
        """
        修改用户密码
        :param request: 请求对象
        :return: JSON
        """
        '''获取请求参数'''
        request_data = json.loads(request.body)  # 获取请求参数
        old_password = request_data.get("old_password")  # 原密码
        new_password = request_data.get("new_password")  # 新密码
        new_password2 = request_data.get("new_password2")  # 确认密码

        '''检查请求参数'''
        if all([old_password, new_password, new_password2]):
            '''验证原密码'''
            user = request.user
            if user.check_password(old_password):
                '''检查新密码格式和是否一致'''
                if (re.match(r"^[a-zA-Z0-9_]{8,20}$", new_password) and
                        re.match(r"^[a-zA-Z0-9_]{8,20}$", new_password2)):  # 检查两次输入的密码的格式
                    if new_password == new_password2:  # 判断两次输入的密码是否一致
                        '''更新用户密码'''
                        user.set_password(new_password)  # 提交修改
                        user.save()  # 执行修改
                        return JsonResponse({'code': 0, 'errmsg': 'OK'})
        return JsonResponse({'code': 400, 'errmsg': '修改密码失败'})


class UserBrowseHistory(LoginRequiredMixin, View):

    def post(self, request):
        """
        保存用户浏览记录
        :param request: 请求对象
        :return: JSON
        """
        '''获取请求参数'''
        request_data = json.loads(request.body)
        sku_id = request_data.get("sku_id")

        '''检查请求参数'''
        try:
            int(sku_id)
        except Exception as e:
            logger.error(e)
            return JsonResponse({"code": 400, "errmsg": "请求参数有误"})

        '''保存数据'''
        user_id = request.user.id  # 获取用户id
        redis_conn = get_redis_connection("history")  # 获取redis存储用户浏览记录的3号库对象
        pip = redis_conn.pipeline()  # 创建管道
        pip.lrem(f"history_{user_id}", 0, sku_id)  # 删除所有已存在的记录
        pip.lpush(f"history_{user_id}", sku_id)  # 保存最新记录
        pip.ltrim(f"history_{user_id}", 0, 4)  # 保留五条数据
        try:
            pip.execute()  # 执行管道
        except Exception as e:
            logger.error(e)
            return JsonResponse({"code": 400, "errmsg": "保存浏览记录失败"})

        '''返回响应'''
        return JsonResponse({"code": 0, "errmsg": "OK"})

    def get(self, request):
        """
        获取用户浏览记录
        :param request: 请求对象
        :return: JSON
        """
        '''从数据库获取浏览记录'''
        user_id = request.user.id
        redis_conn = get_redis_connection("history")
        history = redis_conn.lrange(f"history_{user_id}", 0, -1)

        '''根据记录的商品id获取商品信息'''
        skus = []  # 创建一个列表存储商品信息
        for sku_id in history:
            sku_id = sku_id.decode()
            try:
                sku = SKU.objects.get(id=sku_id)
            except Exception as e:
                logger.error(e)
                print(f"此商品(商品ID:{sku_id})不存在")
                continue
            skus.append({
                "id": sku_id,
                "name": sku.name,
                "default_image_url": sku.default_image_url,
                "price": sku.price
            })

        '''返回响应数据'''
        return JsonResponse({
            "code": 0,
            "errmsg": "OK",
            "skus": skus
        })
